package net.yeah.mickey_zhouyou.database.util;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.lang.reflect.Field;
import java.util.Map.Entry;
import java.util.Set;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import net.yeah.mickey_zhouyou.database.domain.BaseEntity;
import net.yeah.mickey_zhouyou.database.domain.BaseRelationship;
import net.yeah.mickey_zhouyou.database.sequence.Sequence;
import net.yeah.mickey_zhouyou.database.util.new_instance_policy.NewEntityPolicy;
import net.yeah.mickey_zhouyou.database.util.new_instance_policy.NewRelationshipPolicy;
import net.yeah.mickey_zhouyou.database.util.new_instance_policy.NewSequencePolicy;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import org.xml.sax.SAXException;

/**
 * @author Mickey
 * 
 */
public class XMLUtil {

	private static DocumentBuilder docBuilder = null;
	private static Transformer trans = null;

	static {
		try {
			docBuilder = DocumentBuilderFactory.newInstance()
					.newDocumentBuilder();
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		}

		try {
			trans = TransformerFactory.newInstance().newTransformer();
			trans.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
			trans.setOutputProperty(OutputKeys.INDENT, "yes");
			trans.setOutputProperty(OutputKeys.ENCODING, "utf-8");
			trans.setOutputProperty(OutputKeys.METHOD, "xml");
		} catch (TransformerException e) {
			e.printStackTrace();
		}
	}

	public static Document loadDoc(File file) throws SAXException, IOException {
		return docBuilder.parse(file);
	}

	public static String docToXMLString(Document doc)
			throws TransformerException, IOException {
		StringWriter sw = new StringWriter();
		trans.transform(new DOMSource(doc), new StreamResult(sw));
		String res = sw.toString();
		sw.close();
		return res;
	}

	static public void saveDocument(Document doc, File f)
			throws TransformerException, IOException {
		FileOutputStream os = new FileOutputStream(f);
		StreamResult sr = new StreamResult(os);
		trans.transform(new DOMSource(doc), sr);
		os.close();
	}

	static public <R extends BaseRelationship<?, ?>> Document toDoc(R obj) {
		return defaultToDoc(obj);
	}

	static public <E extends BaseEntity> Document toDoc(E obj) {
		return defaultToDoc(obj);
	}

	static public Document toDoc(Sequence seq) {
		return defaultToDoc(seq);
	}

	static public BaseEntity toEntity(Document doc)
			throws InstantiationException, IllegalAccessException,
			ClassNotFoundException {
		return BaseEntity.class.cast(defaultToObj(doc,
				NewEntityPolicy.getPolicy()));
	}

	static public BaseRelationship<?, ?> toRelationship(Document doc)
			throws InstantiationException, IllegalAccessException,
			ClassNotFoundException {
		return BaseRelationship.class.cast(defaultToObj(doc,
				NewRelationshipPolicy.getPolicy()));
	}

	static public Sequence toSequence(Document doc)
			throws InstantiationException, IllegalAccessException,
			ClassNotFoundException {
		return Sequence.class.cast(defaultToObj(doc,
				NewSequencePolicy.getPolicy()));
	}

	static private <T> Document defaultToDoc(T obj) {
		Class<?> clazz = obj.getClass();
		Set<Entry<String, Field>> fs = ClassFieldsCache.getAllFields(clazz);

		Document doc = docBuilder.newDocument();
		Element root = doc.createElement("bean");
		root.setAttribute("class", clazz.getName());

		for (Entry<String, Field> esf : fs) {
			Class<?> type = esf.getValue().getType();
			if (StringUtil.isSampleType(type)) {
				Element property = doc.createElement("property");
				property.setAttribute("name", esf.getKey());
				property.setAttribute("type", type.getName());
				Text value = doc.createTextNode(StringUtil
						.sampleTypeObjToString(ClassFieldsCache.getValue(obj,
								esf.getKey())));
				property.appendChild(value);
				root.appendChild(property);
			}
		}

		doc.appendChild(root);
		return doc;
	}

	static private Object defaultToObj(Document doc, NewInstancePolicy<?> policy)
			throws InstantiationException, IllegalAccessException,
			ClassNotFoundException {
		Element ele = Element.class.cast(doc.getElementsByTagName("bean").item(
				0));
		String className = ele.getAttribute("class");
		NodeList children = ele.getChildNodes();
		Object obj = policy.newInstance(className);

		if (obj == null)
			throw new InstantiationException("实例化出错了：" + className);

		for (int i = 0; i < children.getLength(); ++i) {
			Node node = children.item(i);
			if (node instanceof Element) {
				Element e = Element.class.cast(node);
				String fieldName = e.getAttribute("name");
				String typeName = e.getAttribute("type");
				String value = null;
				Text t = Text.class.cast(e.getFirstChild());
				if (t != null)
					value = t.getData();
				ClassFieldsCache.setValue(obj, fieldName,
						StringUtil.strToSampleTypeObj(value, typeName));
			}
		}
		return obj;
	}
}
